﻿using mozhi.smarterp.Business.Enums;
using mozhi.smarterp.Business.Inventories.Repositories;
using mozhi.smarterp.Business.Products.Repositories;
using Mozhi.Abp.Domain.BasicData;
using Mozhi.Abp.Domain.BasicData.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Services;
using Volo.Abp.Guids;

namespace mozhi.smarterp.Business.Inventories.Services
{
    public class InventoryRealTimeManager : DomainService
    {
        private IProductRepository _productRepository;
        private IProductInventorySettingRepository _inventorySettingRepository;
        private IInventoryRealTimeRepository _repository;
        private IWarehouseRepository _warehouseRepository;
        private IGuidGenerator _guidGenerator;

        public InventoryRealTimeManager(IProductRepository productRepository, IProductInventorySettingRepository inventorySettingRepository, IInventoryRealTimeRepository repository, IGuidGenerator guidGenerator,
            IWarehouseRepository warehouseRepository)
        {
            _productRepository = productRepository;
            _inventorySettingRepository = inventorySettingRepository;
            _repository = repository;
            _warehouseRepository = warehouseRepository;
            _guidGenerator = guidGenerator;
        }

        /// <summary>
        /// 入库计算
        /// </summary>
        /// <param name="productId"></param>
        /// <param name="warehouseId"></param>
        /// <param name="locationId"></param>
        /// <param name="batchNumber"></param>
        /// <param name="serialNumber"></param>
        /// <param name="productionDate"></param>
        /// <param name="quantity"></param>
        /// <returns></returns>
        public async Task<InventoryRealTime> Calculate‌InboundAsync(Guid productId, Guid warehouseId, Guid? locationId, string batchNumber, string serialNumber,
            DateTime? productionDate, decimal quantity, bool autoSave = false)
        {
            var product = await _productRepository.GetAsync(productId);
            var productInventorySetting = await _inventorySettingRepository.GetByProductIdAsync(productId);

            var warehouse = await _warehouseRepository.GetAsync(warehouseId);

            InventoryRealTime inventoryRealTime;

            bool isInsert = false;

            //先进先出，不管是否存在批次，都需要记录一个入库时间值，并且单独作为一条数据
            if (productInventorySetting.PricingType == ProductPricingType.FIFO)
            {
                inventoryRealTime = new InventoryRealTime(_guidGenerator.Create(), product, warehouse, locationId, DateTime.UtcNow, batchNumber, serialNumber,
                    productionDate, productInventorySetting.ShelfLifeDays);
                inventoryRealTime.IncreaseQuantity(quantity);

                isInsert = true;
            }
            else
            {
                inventoryRealTime = await _repository.GetAsync(productId, warehouseId, locationId, batchNumber, serialNumber, productionDate);

                if (inventoryRealTime == null)
                {
                    inventoryRealTime = new InventoryRealTime(_guidGenerator.Create(), product, warehouse, locationId, null, batchNumber, serialNumber, productionDate, productInventorySetting.ShelfLifeDays);
                    isInsert = true;
                }
                inventoryRealTime.IncreaseQuantity(quantity);
            }

            if (autoSave == true)
                inventoryRealTime = isInsert ? await _repository.InsertAsync(inventoryRealTime) : await _repository.UpdateAsync(inventoryRealTime);


            return inventoryRealTime;
        }

        /// <summary>
        /// 出库计算
        /// </summary>
        /// <returns></returns>
        public async Task<List<InventoryRealTime>> Calculate‌OutboundAsync(Guid productId, Guid warehouseId, Guid? locationId, string batchNumber, string serialNumber , DateTime? productionDate, decimal quantity, bool autoSave = false)
        {
            var product = await _productRepository.GetAsync(productId);
            var productInventorySetting = await _inventorySettingRepository.GetByProductIdAsync(productId);

            var warehouse = await _warehouseRepository.GetAsync(warehouseId);

            var result = new List<InventoryRealTime>();

            var inventoryRealTimes = await _repository.GetListAsync(productId, warehouseId, locationId, batchNumber, serialNumber, productionDate);

            if (inventoryRealTimes.Count == 0 || inventoryRealTimes.Sum(x => x.AvailableQuantity) < quantity)
                throw new UserFriendlyException($"产品：{product.Code}-{product.Name}可用库存不足", "100000");

            if (productInventorySetting.PricingType == ProductPricingType.FIFO)
            {
                inventoryRealTimes = inventoryRealTimes.Where(x => x.AvailableQuantity > 0).OrderBy(x => x.InboundDate).ToList();

                var remainingQuantity = quantity;

                foreach (var inventoryRealTime in inventoryRealTimes)
                {
                    if (remainingQuantity == 0)
                        break;

                    if (inventoryRealTime.AvailableQuantity >= quantity)
                    {
                        remainingQuantity = 0;

                        inventoryRealTime.DecreaseQuantity(quantity);
                    }
                    else
                    {
                        inventoryRealTime.DecreaseQuantity(inventoryRealTime.AvailableQuantity);
                        remainingQuantity = remainingQuantity - inventoryRealTime.AvailableQuantity;
                    }

                    result.Add(inventoryRealTime);
                }
            }
            else
            {
                //其他方式计价，同一产品，同一仓库库位，统一批号，序列号只能有一条数据
                var inventoryRealTime = inventoryRealTimes.First();

                inventoryRealTime.DecreaseQuantity(quantity);

                result.Add(inventoryRealTime);
            }

            if(autoSave)
                await _repository.UpdateManyAsync(result);

            return result;
        }


        #region

        #endregion
    }
}
